# # -*- coding: utf-8 -*-
# # @FileName : RNN股票预测.py
# # @Author   : wangxinyao
# # @Time     : 2025/2/19 09:20
# 二、功能需求
# 1.数据读取与处理
import matplotlib.pyplot as plt
import numpy as  np
import torch
import warnings

# 1)数据必须从data-02-stock_daily.csv文件中读取，且该文件必须存在于代码执行的当前目录中。
loadtxt=np.loadtxt('data-02-stock_daily.csv',delimiter=',')
print(loadtxt)
# 2)数据需要被反转（data = data[::-1]），这可能是为了满足时间序列分析中对最新数据的需求。
data=loadtxt[::-1]
print(data)
# 3)数据需要经过归一化处理，使用MinMaxScaler进行缩放。
from sklearn.preprocessing import MinMaxScaler
data=MinMaxScaler().fit_transform(data)
# 4)数据需要被划分为特征（x）和目标（y），其中特征是由连续7天的数据构成，目标是第8天的收盘价。
c=7
x=[]
y=[]
for i in range(len(data)-c):
    x.append(data[i:i+c])
    y.append(data[i+c,-1])
# 5)数据需要被转换为PyTorch张量，并且目标变量需要被重塑为(-1, 1)的形状。
x=torch.tensor(x,dtype=torch.float)
y=torch.tensor(y,dtype=torch.float).reshape(-1,1)
# 6)数据集需要被划分为训练集和测试集，且划分时不进行随机打乱（shuffle=False）。
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,shuffle=False)
# 2.模型结构与参数
# 1)模型必须是一个两层的LSTM网络，第一层LSTM输出5个神经元，第二层LSTM输出10个神经元。
# 2)LSTM网络的第一层之后需要进行dropout处理，失活比例为0.2（尽管代码中并未明确包含dropout层，但这是一个约束条件，需要在模型中实现）。
# 3)模型的最后一层必须是一个全连接层，输出维度为1，使用线性激活函数。
class LSTM(torch.nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.lstm1=torch.nn.LSTM(input_size=x.shape[2],hidden_size=5,batch_first=True)
        self.lstm2=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.lstm3=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.lstm4=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.linear1 = torch.nn.Linear(in_features=5, out_features=1)
    def forward(self,x):
        x,_=self.lstm1(x)
        x,_=self.lstm2(x)
        x,_=self.lstm3(x)
        out=self.linear1(x[:,-1,:])
        return out
'''
class LSTM(torch.nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.lstm1=torch.nn.LSTM(input_size=x.shape[2],hidden_size=5,batch_first=True)
        self.linear1 = torch.nn.Linear(in_features=5, out_features=1)
    def forward(self,x):
        x,_=self.lstm1(x)

        out=self.linear1(x[:,-1,:])
        return out
        第一层(0.0008)
        
class LSTM(torch.nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.lstm1=torch.nn.LSTM(input_size=x.shape[2],hidden_size=5,batch_first=True)
        self.lstm2=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)

        self.linear1 = torch.nn.Linear(in_features=5, out_features=1)
    def forward(self,x):
        x,_=self.lstm1(x)

        out=self.linear1(x[:,-1,:])
        return out
        第二层(0.0006)
        
class LSTM(torch.nn.Module):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.lstm1=torch.nn.LSTM(input_size=x.shape[2],hidden_size=5,batch_first=True)
        self.lstm2=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.lstm3=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.lstm4=torch.nn.LSTM(input_size=5,hidden_size=5,batch_first=True)
        self.linear1 = torch.nn.Linear(in_features=5, out_features=1)
    def forward(self,x):
        x,_=self.lstm1(x)
        x,_=self.lstm2(x)
        x,_=self.lstm3(x)
        out=self.linear1(x[:,-1,:])
        return out
        d第三层(0.0010)
        
        第三层损失率最低
'''

# 3.训练与优化
# 1)代价函数必须是均方误差（MSE）。
# 2)优化器必须是Adam，学习率设置为0.01。
# 3)训练过程中需要记录损失值，以便后续分析。
# 4)训练轮次固定为1000次。
model=LSTM()
mse_loss=torch.nn.MSELoss()
adam=torch.optim.Adam(model.parameters(),lr=0.01)
'''
adam=torch.optim.Adam(model.parameters(),lr=0.0001)
损失率为0.1764
adam=torch.optim.Adam(model.parameters(),lr=0.001)
损失率为0.0037
adam=torch.optim.Adam(model.parameters(),lr=0.01)
损失率为0.0019
0。001成本低
'''
model.train()
loss_list=[]
for i in range(1000):
    adam.zero_grad()
    h=model(x_train)
    loss=mse_loss(h,y_train)
    loss.backward()
    loss_list.append(loss)
    adam.step()
    if i%10==0:
        print(i,loss)
# 4.预测与评估
# 1)预测结果需要从测试集中获得，并与真实的测试集目标值进行比较。
# 2)预测结果和真实值需要使用matplotlib库绘制成图像进行对比。
model.eval()
with torch.no_grad():
    h=model(x_test)
    plt.plot(loss_list)
    plt.show()
    plt.plot(h)
    plt.plot(y_test)
    plt.show()
    loss=mse_loss(h,y_test)
    print(loss)
'''真实值和预测值 都存在差异 后续通过调整模型数据进行修改'''
